Přeskočit na hlavní obsah

Úvod do Qiskitu

V tomto notebooku prozkoumáme, jak programovat kvantové brány a kvantové Circuit v Qiskitu, a dokonce jak je spouštět na simulátorech a skutečných kvantových počítačích pomocí vzorů Qiskit. Později si představíme různé způsoby kódování informací a zakončíme bonusovým příkladem kvantové teleportace.

Než začneš

Pokud jsi tak ještě neučinil/a, postupuj podle pokynů k instalaci a nastavení, včetně kroků pro nastavení pro použití IBM Quantum™ Platform.

Doporučujeme používat vývojové prostředí Jupyter pro práci s kvantovými počítači. Nezapomeň nainstalovat doporučenou dodatečnou podporu vizualizace ('qiskit[visualization]'). Pro druhou část tohoto příkladu budeš také potřebovat balíček matplotlib.

Pokud se chceš dozvědět více o kvantovém výpočtu obecně, navštiv kurz Základy kvantových informací na IBM Quantum Learning

Importy

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime
# Import necessary modules for this notebook
import time
import qiskit

from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector, plot_state_qsphere
from qiskit_aer import AerSimulator
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.visualization import plot_histogram
print(qiskit.__version__)
2.3.1

Aby ses mohl/a spouštět kvantové obvody na hardwaru, musíš si nejprve nastavit účet. Postup je následující:

  1. Přejdi na vylepšenou IBM Quantum® Platform.
  2. Přejdi do pravého horního rohu (jak je zobrazeno na obrázku výše), vytvoř svůj API token a zkopíruj ho na bezpečné místo.
  3. V další buňce nahraď deleteThisAndPasteYourAPIKeyHere svým API klíčem.
  4. Přejdi do levého dolního rohu (jak je zobrazeno na obrázku výše) a vytvoř svou instanci. Ujisti se, že zvolíš otevřený plán.
  5. Po vytvoření instance zkopíruj přiřazený CRN kód. Možná bude nutné stránku obnovit, aby se instance zobrazila.
  6. V buňce níže nahraď deleteThisAndPasteYourCRNHere svým CRN kódem.

Více podrobností o nastavení účtu IBM Cloud® najdeš v tomto průvodci.

⚠️ Poznámka: Zacházej se svým API klíčem jako s bezpečným heslem. Více informací o používání API klíče v bezpečných i nedůvěryhodných prostředích najdeš v průvodci Nastavení cloudu.

#your_api_key = "deleteThisAndPasteYourAPIKeyHere"
#your_crn = "deleteThisAndPasteYourCRNHere"

QiskitRuntimeService.save_account(
channel="ibm_quantum_platform",
token=your_api_key,
instance=your_crn,
overwrite=True
)

1. Kvantové Gate a kvantové Circuit

Kvantové Circuit jsou modely pro kvantový výpočet, v nichž výpočet tvoří posloupnost kvantových Gate. Podívejme se na některé z populárních kvantových Gate.

X Gate

X Gate odpovídá rotaci kolem osy X Blochovy sféry o π\pi radiánů. Zobrazuje 0|0\rangle na 1|1\rangle a 1|1\rangle na 0|0\rangle. Je to kvantový ekvivalent brány NOT pro klasické počítače a někdy se nazývá bit-flip.

X=(0110)X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix}

# Let's apply an X-gate on a |0> qubit
qc = QuantumCircuit(1)
qc.x(0)
qc.draw(output='mpl')

Quantum circuit diagram

# Let's see Bloch sphere visualization
sv = Statevector(qc)
plot_bloch_multivector(sv)

Code output

H Gate

Hadamardova brána představuje rotaci o π\pi kolem osy, která leží uprostřed mezi osou XX a osou ZZ. Zobrazuje bázový stav 0|0\rangle na 0+12\frac{|0\rangle + |1\rangle}{\sqrt{2}}, což znamená, že měření bude mít stejné pravděpodobnosti pro výsledek 1 nebo 0, čímž vzniká 'superpozice' stavů. Tento stav se také zapisuje jako +|+\rangle.

H=12(1111)H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \\ \end{pmatrix}

# Let's apply an H-gate on a |0> qubit
qc = QuantumCircuit(1)
qc.x(0)
qc.h(0)
qc.draw(output='mpl')

Quantum circuit diagram

# Let's see Bloch sphere visualization
sv = Statevector(qc)
plot_bloch_multivector(sv)

Code output

CX Gate (CNOT Gate)

Řízená NOT brána (neboli CNOT nebo CX) působí na dva Qubit. Provede operaci NOT (ekvivalentní aplikaci X Gate) na druhém Qubit pouze tehdy, když první Qubit je 1|1\rangle, jinak ho nechá beze změny. Poznámka: Qiskit čísluje bity v řetězci zprava doleva.

CX=(1000010000010010)CX = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ \end{pmatrix}

# Let's apply a CX-gate on |11>
qc = QuantumCircuit(2)
qc.x(0)
qc.x(1)
qc.cx(0,1)
qc.draw(output='mpl')

Quantum circuit diagram

sv=Statevector(qc)
plot_state_qsphere(sv)

Code output

Vytvoř první Bellův stav

ϕ+=12(00+11)|\phi^+ \rangle = \frac{1}{\sqrt 2}(|00 \rangle + |11 \rangle)

# Create a Bell state circuit

qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)

# Draw the circuit
qc.draw("mpl")

Quantum circuit diagram

# Plot the state using q-sphere visualization
sv = Statevector(qc)
plot_state_qsphere(sv)
# q-sphere is useful for visualizing states when Bloch sphere fails to

Code output

Vytvoř druhý Bellův stav

ϕ=12(0011)|\phi^- \rangle = \frac{1}{\sqrt 2}(|00 \rangle - |11 \rangle)

# Create a circuit with the second Bell state

qc = QuantumCircuit(2)
qc.x(0)
qc.h(0)
qc.cx(0,1)

qc.draw("mpl")

Quantum circuit diagram

Vysvětlení spočívá v tom, že:

H1=12(01)=H|1\rangle=\frac{1}{\sqrt{2} }(|0\rangle-|1\rangle) = |-\rangle
# Get the statevector of the circuit
sv = Statevector(qc)

# Plot the state using qsphere visualization
plot_state_qsphere(sv)

Quantum circuit diagram

Vytvoř 3-qubitový stav GHZ

GHZ=12(000+111)|GHZ \rangle = \frac{1}{\sqrt 2}(|000 \rangle + |111 \rangle)

# Create a circuit with 3-qubit GHZ state

qc= QuantumCircuit(3)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)

qc.draw("mpl")

Quantum circuit diagram

# Get the statevector of the circuit
sv = Statevector(qc)

# Plot the state using qsphere visualization
plot_state_qsphere(sv)

Quantum circuit diagram

Vytvoř stav loga Qiskit

Qiskit=12(0010+1101)|Qiskit \rangle = \frac{1}{\sqrt 2}(|0010 \rangle + |1101 \rangle)

Centered Image
# Create a circuit with the Qiskit logo state

qc = QuantumCircuit(4)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
qc.cx(0,3)
qc.x(1)

# Draw the circuit
qc.draw("mpl")

Quantum circuit diagram

# Get the statevector of the circuit
sv = Statevector(qc)

# Plot the state using qsphere visualization
plot_state_qsphere(sv)

Quantum circuit diagram

2. Vytvoř a spusť jednoduchý kvantový program

Čtyři kroky k napsání kvantového programu pomocí vzorů Qiskit jsou:

  1. Mapuj problém do formátu nativního pro kvantové počítače.

  2. Optimalizuj Circuit a operátory.

  3. Spusť pomocí primitivní kvantové funkce.

  4. Analyzuj výsledky.

2.1 Map the problem to a quantum-native format

V kvantovém programu jsou kvantové obvody nativním formátem pro reprezentaci kvantových instrukcí a operátory reprezentují pozorovatelné veličiny, které se mají měřit. Při vytváření obvodu obvykle vytvoříš nový objekt QuantumCircuit a poté do něj postupně přidáváš instrukce.

Následující buňka kódu vytváří obvod, který produkuje stav GHZ – stav, ve kterém jsou tři qubity plně provázány navzájem.

Qiskit SDK používá číslování bitů LSb 0, kde nthn^{th} číslice má hodnotu 1n1 \ll n nebo 2n2^n. Více podrobností najdeš v tématu Bit-ordering in the Qiskit SDK.

# Create a GHZ state circuit

qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
# Draw the circuit
qc.draw("mpl")

Quantum circuit diagram

V dokumentaci najdeš všechny dostupné operace v sekci QuantumCircuit.

Při vytváření kvantových obvodů musíš také zvážit, jaký typ dat chceš po provedení vrátit. Qiskit poskytuje dva způsoby, jak vrátit data: můžeš získat rozdělení pravděpodobnosti pro sadu qubitů, které se rozhodneš měřit, nebo můžeš získat střední hodnotu pozorovatelné veličiny. Připrav svou úlohu tak, aby měřila tvůj obvod jedním z těchto dvou způsobů pomocí primitiv Qiskit (podrobně vysvětleno v kroku 3).

Tento příklad měří střední hodnoty pomocí submodulu qiskit.quantum_info, který je specifikován pomocí operátorů (matematické objekty používané k reprezentaci akce nebo procesu, který mění kvantový stav). Následující buňka kódu vytváří šest tříqubitových Pauliho operátorů: ZZZ, ZZX, ZII, XXI, ZZI a III.

# Set up six different observables.

observables_labels = ["ZZZ", "ZZX", "ZII", "XXI", "ZZI", "III"]

observables = [SparsePauliOp(label) for label in observables_labels]
print(observables)
[SparsePauliOp(['ZZZ'],
coeffs=[1.+0.j]), SparsePauliOp(['ZZX'],
coeffs=[1.+0.j]), SparsePauliOp(['ZII'],
coeffs=[1.+0.j]), SparsePauliOp(['XXI'],
coeffs=[1.+0.j]), SparsePauliOp(['ZZI'],
coeffs=[1.+0.j]), SparsePauliOp(['III'],
coeffs=[1.+0.j])]

Zde je například operátor ZZI zkráceným zápisem pro tenzorový součin ZZIZ\otimes Z\otimes I, což znamená měření Z na qubitu 2 a Z na qubitu 1 dohromady, přičemž získáváme informace o korelaci mezi qubitem 2 a qubitem 1. Střední hodnoty jako tato se také typicky zapisují jako Z2Z1\langle Z_2 Z_1 \rangle.

Pokud je stav, který pozorujeme, tříqubitový stav GHZ, pak by měření Z2Z1\langle Z_2 Z_1 \rangle mělo být 1.

2.2 Optimize the circuits and operators

Při provádění obvodů na zařízení je důležité optimalizovat sadu instrukcí, které obvod obsahuje, a minimalizovat celkovou hloubku (přibližně počet instrukcí) obvodu. Tím zajistíš co nejlepší výsledky snížením vlivu chyb a šumu. Instrukce obvodu navíc musí odpovídat architektuře instrukční sady (ISA) backendového zařízení a musí zohledňovat základní Gate zařízení a propojení qubitů.

Následující kód vytvoří instanci skutečného zařízení, na které se odešle úloha, a transformuje obvod a operátory tak, aby odpovídaly ISA daného backendu. Pokud jsi dříve neuložil/a své přihlašovací údaje, postupuj podle pokynů zde pro ověření pomocí svého API tokenu.

# Choose a real backend
service = QiskitRuntimeService(channel='ibm_quantum_platform',)
backend = service.least_busy(min_num_qubits=156)
# print backend details
print(
f"Name: {backend.name}\n"
f"Version: {backend.backend_version}\n"
f"No. of qubits: {backend.num_qubits}\n"
f"Processor type: {backend.processor_type}\n"
)
Name: ibm_marrakesh
Version: 1.0.21
No. of qubits: 156
Processor type: {'family': 'Heron', 'revision': '2'}
# option to use the AerSimulator instead of a real quantum device
seed_sim=42
backend=AerSimulator.from_backend(backend,seed_simulator=seed_sim)

Transpiluj obvod do ISA obvodu

# Convert to an ISA circuit and layout-mapped observables.

pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
isa_circuit = pm.run(qc)

isa_circuit.draw("mpl", idle_wires=False)

Quantum circuit diagram

mapped_observables = [
observable.apply_layout(isa_circuit.layout) for observable in observables
]
print(mapped_observables)
[SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIXIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIXIIIIIIIIIIIIIIIIIIXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])]

2.3 Execute using the quantum primitives

Kvantové počítače mohou produkovat náhodné výsledky, takže obvykle shromažďuješ vzorek výstupů opakovaným spouštěním obvodu. Střední hodnotu pozorovatelné veličiny můžeš odhadnout pomocí třídy Estimator. Estimator je jedním ze dvou primitiv; druhým je Sampler, který lze použít k získání dat z kvantového počítače. Tyto objekty mají metodu run(), která spouští výběr obvodů, pozorovatelných veličin a parametrů (pokud jsou relevantní) pomocí primitive unified bloc (PUB). Při spouštění tohoto kódu na skutečném kvantovém hardwaru zvažte použití technik pro potlačení a zmírnění chyb ke snížení vnitřního šumu kvantového počítače.

# Construct the Estimator instance.
estimator = Estimator(mode=backend)
estimator.options.resilience_level = 1
estimator.options.default_shots = 5000

Odešli úlohu pomocí primitiva Estimator.

# One pub, with one circuit to run against six different observables.
job = estimator.run([(isa_circuit, mapped_observables)])

# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job.job_id()}")
>>> Job ID: 97ecd036-1767-49b0-a1dc-c71638c3c3c4
/Users/jma/miniconda3/envs/3122/lib/python3.12/site-packages/qiskit_ibm_runtime/fake_provider/local_service.py:187: UserWarning: The resilience_level option has no effect in local testing mode.
warnings.warn("The resilience_level option has no effect in local testing mode.")

Po odeslání úlohy můžeš buď počkat, dokud není úloha dokončena ve stávající instanci Pythonu, nebo použít job_id k pozdějšímu získání dat. (Podrobnosti najdeš v sekci o načítání úloh.)

Po dokončení úlohy prozkoumej její výstup prostřednictvím atributu result() úlohy.

# This is the result of the entire submission.  You submitted one Pub,
# so this contains one inner result (and some metadata of its own).
job_result = job.result()

# This is the result from our single pub, which had six observables,
# so contains information on all six.
pub_result = job.result()[0]

Nyní můžeme také spustit obvod pomocí primitiva Sampler

# We include the measurements in the circuit
qc.measure_all()
sampler = Sampler(mode=backend)
qc.draw(output="mpl")

Quantum circuit diagram

Odešli úlohu pomocí primitiva Sampler.

job_sampler = sampler.run(pm.run([qc]))

# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job_sampler.job_id()}")
# Get the results
results_sampler = job_sampler.result()
>>> Job ID: a6ee4d2f-c80d-4a86-9a76-e4b1a74502e7

2.4 Analyzuj výsledky

Krok analýzy je zpravidla místo, kde výsledky dodatečně zpracuješ – například pomocí zmírňování chyb měření nebo extrapolace nulového šumu (ZNE). Tyto výsledky pak můžeš předat do dalšího pracovního postupu k dalšímu rozboru, nebo připravit graf klíčových hodnot a dat. Obecně platí, že tento krok je specifický pro tvůj konkrétní problém. V tomto příkladu vykresli každou z očekávaných hodnot, které byly pro náš Circuit změřeny.

Očekávané hodnoty a směrodatné odchylky pro observabely, které jsi zadal/a do Estimatoru, jsou přístupné přes atributy PubResult.data.evs a PubResult.data.stds výsledku jobu. Chceš-li získat výsledky ze Sampleru, použij funkci PubResult.data.meas.get_counts(), která vrátí dict měření v podobě bitových řetězců jako klíčů a počtů jako jejich odpovídajících hodnot. Více informací najdeš v části Začínáme se Samplerem.

# Plot the result
from matplotlib import pyplot as plt
values = pub_result.data.evs
errors = pub_result.data.stds
# plotting graph
# Plotting with error bars
plt.errorbar(observables_labels, values, yerr=errors, fmt='-o', capsize=5)
plt.xlabel("Observables")
plt.ylabel("Values")
plt.title("Plot of Observables vs Values with Error Bars")
plt.grid(True)
plt.tight_layout()
plt.show()

Plot output

Vidíme, že observabely ZZIZZI a IIIIII mají očekávanou hodnotu 1 – ZZIZZI totiž zavádí dvě záporná znaménka, která se navzájem vyruší, a IIIIII působí jako identita, takže stav GHZ zůstane nezměněn. Ostatní observabely mají očekávanou hodnotu 0, protože jejich operátory ZZ zavádějí lichý počet záporných znamének, nebo operátory XX překlopí takový počet qubitů, že se překrývající se stavy stanou ortogonálními.

Nyní vykreslíme výsledky pro Sampler.

counts_list = results_sampler[0].data.meas.get_counts()
print(counts_list)
print(f"Outcomes : {counts_list}")
display(plot_histogram(counts_list, title="GHZ state"))
{'111': 480, '000': 503, '101': 8, '100': 9, '001': 3, '011': 6, '010': 10, '110': 5}
Outcomes : {'111': 480, '000': 503, '101': 8, '100': 9, '001': 3, '011': 6, '010': 10, '110': 5}

Code output

2.5 Škálování na velký počet qubitů

V kvantovém počítání je práce na úrovni utility (utility-scale) klíčová pro pokrok v oboru. Taková práce vyžaduje výpočty v mnohem větším měřítku – práci s Circuity, které mohou využívat více než 100 Qubitů a více než 1000 Gate. Tento příklad dělá malý krůček tímto směrem a škáluje problém GHZ na n=10n=10 Qubitů. Využívá pracovní postup Qiskit patterns a na závěr měří očekávanou hodnotu Z0Zi\langle Z_0 Z_i \rangle .

Krok 1. Namapuj problém

Napiš funkci, která vrátí QuantumCircuit připravující nn-qubitový stav GHZ (v podstatě rozšířený Bellův stav), pak tuto funkci použij k přípravě 10-qubitového stavu GHZ a sesbírej observabely, které chceš měřit.

def get_qc_for_n_qubit_GHZ_state(n: int) -> QuantumCircuit:

qc = QuantumCircuit(n)
qc.h(0)
for i in range(n-1):
qc.cx(i, i+1)
return qc
n = 10
qc_n_GHZ = get_qc_for_n_qubit_GHZ_state(n)
qc_n_GHZ.draw("mpl")

Quantum circuit diagram

Dále namapuj operátory, které tě zajímají. Tento příklad používá operátory ZZ mezi Qubity ke zkoumání chování při jejich rostoucí vzdálenosti. Čím méně přesné (zkreslené) očekávané hodnoty mezi vzdálenými Qubity by odhalily míru přítomného šumu.

# ZZII...II, ZIZI...II, ... , ZIII...IZ
operator_strings = [
"Z" + i * "I" + "Z" + "I" * (n-i-2) for i in range(n-1)
]
print(operator_strings)
print(len(operator_strings))

operators = [SparsePauliOp(operator) for operator in operator_strings]
['ZZIIIIIIII', 'ZIZIIIIIII', 'ZIIZIIIIII', 'ZIIIZIIIII', 'ZIIIIZIIII', 'ZIIIIIZIII', 'ZIIIIIIZII', 'ZIIIIIIIZI', 'ZIIIIIIIIZ']
9

Krok 2. Optimalizuj problém pro spuštění na kvantovém Backendu

Transformuj Circuit a observabely tak, aby odpovídaly ISA Backendu.

# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
isa_circuit = pm.run(qc_n_GHZ)
isa_operators_list = [operator.apply_layout(isa_circuit.layout) for operator in operators]

Krok 3. Spusť na Backendu

Odešli job, a pokud ho spouštíš na hardwaru, zapni potlačení chyb pomocí techniky zvané dynamical decoupling. Úroveň odolnosti (resilience level) určuje, jak silnou ochranu před chybami chceš vybudovat. Vyšší úrovně poskytují přesnější výsledky, ale za cenu delší doby zpracování. Další vysvětlení možností nastavených v následujícím kódu najdeš v části Konfigurace zmírňování chyb pro Qiskit Runtime.

# Submit the circuit to Estimator
job = estimator.run([(isa_circuit, isa_operators_list)])
job_id = job.job_id()
/Users/jma/miniconda3/envs/3122/lib/python3.12/site-packages/qiskit_ibm_runtime/fake_provider/local_service.py:187: UserWarning: The resilience_level option has no effect in local testing mode.
warnings.warn("The resilience_level option has no effect in local testing mode.")

Krok 4. Dodatečně zpracuj výsledky

Abychom lépe pochopili chování provázaných kvantových stavů na reálném hardwaru, analyzujeme párové korelace mezi Qubity v bázi Z. Konkrétně se zaměříme na očekávané hodnoty ⟨Z₀Zᵢ⟩, které měří, jak silně je Qubit 0 korelován s každým dalším Qubitem i. Budeme zejména kreslit:

ZiZ0/Z1Z0\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle

Jaké hodnoty ZiZ0/Z1Z0\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle očekáváš v grafu?

Možnosti:

a) Klesající s rostoucím ii

b) Konstantní, rovno 1

c) Malé odchylky kolem 1

d) Střídavě 1 a 0 pro liché a sudé hodnoty ii

data = list(range(1, len(operators) + 1))  # Distance between the Z operators
result = job.result()[0]
values = result.data.evs # Expectation value at each Z operator.
values = [
v / values[0] for v in values
] # Normalize the expectation values to evaluate how they decay with distance.

plt.plot(data, values, marker="o", label=f"{n}-qubit GHZ state")
plt.xlabel("Distance between qubits $i$")
plt.ylabel(r"$\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle $")
plt.legend()
plt.show()

Plot output

V tomto grafu si všimni, že Z0Zi\langle Z_0 Z_i \rangle kolísá kolem hodnoty 1, přestože v ideální simulaci by všechny Z0Zi\langle Z_0 Z_i \rangle měly být rovny 1.

Jak vidíš, výsledky experimentů s 10 Qubity jsou dobré, ale stále obsahují určité chyby. Jedním ze způsobů, jak výsledky zlepšit, je implementovat stav GHZ efektivněji.

Obvykle se stav GHZ implementuje schodišťovou posloupností CNOT Gate. Lze ho ale implementovat efektivněji – hloubku 2-qubitových Gate lze snížit z n na n/2 nebo méně.

Jednou z důležitých metrik pro posouzení přesnosti výsledků, respektive míry šumu v Circuitu, je hloubka 2-qubitových Gate. Je tomu tak proto, že míry chyb 2-qubitových Gate (přibližně 10× vyšší než u jednoQubitových Gate) dominují celkovým chybám Circuitu. K získání hloubky 2-qubitových Gate Circuitu použij následující kód.

qc.depth(lambda x: x.operation.num_qubits == 2)
def better_ghz(n):
"fan out"
s = int(n / 2)
qc = QuantumCircuit(n)
qc.h(s)
for m in range(s, 0, -1):
qc.cx(m, m - 1)
if not (n % 2 == 0 and m == s):
qc.cx(n - m - 1, n - m)
return qc

better_ghz(n).draw("mpl")

Quantum circuit diagram

# Check 2-qubit gate depth before transpilation
qc_better_ghz = better_ghz(n)
qc_better_ghz.depth(lambda x: x.operation.num_qubits == 2)
5

Zajímavé je, že se nám podařilo snížit kvantovou hloubku Circuitu, který chceme spustit, pouhou chytrostí a promyšlením jiného způsobu jeho naprogramování. Nastanou ale situace a algoritmy, kdy se na tyto chytré triky nemůžeme spoléhat. Právě zde přichází vhod Transpiler – pomáhá nám efektivně optimalizovat všechny tyto aspekty, abychom se o ně nemuseli příliš starat.

3. Kódování informace

3.1 Amplitude encoding

Teď, když jsme viděli, jak sestavovat kvantové obvody, je zajímavé prozkoumat, jak můžeme kódovat klasické informace do kvantových stavů. Jednou z výkonných metod je amplitudové kódování, kde amplitudy kvantového stavu reprezentují složky klasického vektoru.

Uvažujme jednoduchý příklad. Předpokládejme, že chceme zakódovat klasický vektor

x=[x0x1x2x3]\vec{x} = \begin{bmatrix} x_0 \\ x_1 \\ x_2 \\ x_3 \end{bmatrix}

do kvantového stavu dvou qubitů. Cílem je připravit kvantový stav:

ψ=x000+x101+x210+x311\ket{\psi} = x_0\ket{00} + x_1\ket{01} + x_2\ket{10} + x_3\ket{11}

kde x0,x1,x2,x3Rx_0, x_1, x_2, x_3 \in \mathbb{R} (nebo C\mathbb{C}) a vektor je normalizován tak, aby platilo:

x02+x12+x22+x32=1|x_0|^2 + |x_1|^2 + |x_2|^2 + |x_3|^2 = 1

Nyní uvažujeme konkrétní příklad: x=[0.8924,0.3696,0.2391,0.0990]\vec{x} = [0.8924, 0.3696, 0.2391, 0.0990]

Odpovídající kvantový stav je pak:

ψ=0.892400+0.369601+0.239110+0.099011\begin{aligned} \ket{\psi} &= 0.8924\,\ket{00} + 0.3696\,\ket{01} + 0.2391\,\ket{10} + 0.0990\,\ket{11} \end{aligned}

Tento stav lze připravit pomocí kombinace rotačních Gate RyR_y o úhlech π/6\pi/6 a π/4\pi/4 pro qubit 0, resp. qubit 1.

from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
import numpy as np

qc = QuantumCircuit(2)

qc.ry(np.pi / 6, 0)
qc.ry(np.pi / 4, 1)

simulator = AerSimulator()
qc.save_statevector()
result = simulator.run(qc).result()
statevector = result.get_statevector()

print("Statevector:", statevector)
qc.draw(output="mpl")
Statevector: Statevector([0.8923991 +0.j, 0.23911762+0.j, 0.36964381+0.j,
0.09904576+0.j],
dims=(2, 2))

Quantum circuit diagram

from qiskit.quantum_info import Statevector

# Define our vector
v = np.array([0.8924, 0.3696, 0.2391, 0.0990])
v = v/np.linalg.norm(v)
# Create a statevector from the vector
state = Statevector(v)

# Initialize a quantum circuit with 2 qubits
qc = QuantumCircuit(2)
qc.initialize(state.data, [0, 1])

# Optional: simulate the state
print("Statevector:", state)

# Visualize the circuit
qc.decompose().decompose().decompose().decompose().decompose().draw("mpl")
Statevector: Statevector([0.89242154+0.j, 0.36960892+0.j, 0.23910577+0.j,
0.09900239+0.j],
dims=(2, 2))

Quantum circuit diagram

Viděli jsme tedy, jak kódovat informace pomocí rotačních Gate.

3.2 Angle encoding a parametrizované obvody

Zvláště zajímavým způsobem kódování informací do kvantového počítače je navrhování kvantového Circuit, který obsahuje určité rotační úhly θ\vec{\theta} nebo parametry, jež lze ladit tak, aby reprezentovaly rodinu funkcí f(θ)f(\vec{\theta}). Uvažujme například následující parametrizovaný kvantový Circuit:

from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

# Define a symbolic parameter
theta = Parameter("θ")

qc = QuantumCircuit(2)
# We applied a parametrized RX gate
qc.rx(theta, 0)
qc.cx(0, 1)
qc.draw("mpl")

Quantum circuit diagram

Matematicky můžeme analyzovat, jaká rodina funkcí je tímto obvodem reprezentovatelná:

CNOT01Rx{0}(θ)00=CNOT01(cos(θ/2)00isin(θ/2)10)=cos(θ/2)00isin(θ/2)11\text{CNOT}_{01} \, R_x^{\{0\}}(\theta) |00\rangle = \text{CNOT}_{01} \left( \cos(\theta/2)\ket{00} - i\sin(\theta/2)\ket{10} \right) = \cos(\theta/2)\ket{00} - i\sin(\theta/2)\ket{11}

Je zcela zřejmé, že počet stavů, které lze tímto kvantovým Circuit reprezentovat, je omezený — například stavy 10\ket{10} nebo 01\ket{01} reprezentovat nelze. Rodina stavů, které lze reprezentovat, se však začne rozrůstat, jakmile zavedeme více rotací na vhodných místech:

from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

# Define a symbolic parameter
theta1 = Parameter("θ1")
theta2 = Parameter("θ2")

qc = QuantumCircuit(2)
qc.rx(theta1, 0)
qc.rx(theta2, 1)
qc.cx(0, 1)
qc.draw("mpl")

Quantum circuit diagram

V tomto případě budou kvantové stavy, které budeme reprezentovat, vypadat takto:

\begin{align*} \text{CNOT}_{01} \, R_x^{\{1}}(\theta_2) R_x^{\{0}}(\theta_1) \ket{00} &= \text{CNOT}_{01} \, R_x^{\{1}}(\theta_2)\left( \cos(\theta_1/2)\ket{00} - i\sin(\theta_1/2)\ket{10} \right) \\ &= \text{CNOT}_{01}\left( \cos(\theta_1/2)\cos(\theta_2/2)\ket{00} - i\cos(\theta_1/2)\sin(\theta_2/2)\ket{01} \right. \\ &\quad \left. - i\sin(\theta_1/2)\cos(\theta_2/2)\ket{10} + \sin(\theta_1/2)\sin(\theta_2/2)\ket{11} \right) \\ &= \cos(\theta_1/2)\cos(\theta_2/2)\ket{00} - i\cos(\theta_1/2)\sin(\theta_2/2)\ket{01} \\ &\quad + \sin(\theta_1/2)\sin(\theta_2/2)\ket{10} - i\sin(\theta_1/2)\cos(\theta_2/2)\ket{11} \end{align*}

Vidíme, že tento Circuit generuje širší rodinu kvantových stavů než ten předchozí. Konkrétně nyní dokáže produkovat stavy s nenulovými amplitudami pro 01\ket{01} nebo 10\ket{10}, které s výše uvedeným Circuit nebyly možné. Tento Circuit však stále není univerzálním generátorem kvantových stavů, i když může být dostatečně výrazný pro navrhování obvodů s určitou flexibilitou pro reprezentaci jistých funkcí. Obecně platí, že čím více nezávislých parametrů (úhlů) zavedeme, tím větší expresivitu Circuit získá pro aproximaci libovolných kvantových stavů.

Ansatzes a knihovna Circuit

Tento druh parametrizovaného kvantového Circuit lze využít k sestavení Ansatzes — zkušebních kvantových stavů, jejichž cílem je aproximovat řešení daného problému. Tyto Ansatzes jsou ústřední součástí Variačních kvantových algoritmů, třídy hybridních kvantově-klasických algoritmů, které využívají kvantový počítač k vyhodnocení účelové funkce a klasický optimalizátor k její minimalizaci. Těmto tématům se budeme podrobně věnovat v pozdější části, ale prozatím si ukážeme, jak sestavit jednoduchý ansatz pomocí knihovny Circuit v Qiskit.

from qiskit.circuit.library import efficient_su2

SU2_ansatz = efficient_su2(4, su2_gates=["rx", "y"], entanglement="linear", reps=1)
SU2_ansatz.decompose().draw(output="mpl")

Quantum circuit diagram

Viděli jsme, jak sestavit jednoduchý Ansatz pomocí funkce efficient_su2 z modulu qiskit.circuit.library, která dokáže generovat širokou škálu kvantových stavů laděním svých parametrů θ\vec{\theta}.

Závěr

V tomto notebooku ses naučil/a sestavovat kvantové Circuit — od budování kvantových Gate až po definování a měření observables — a jak tyto Circuit efektivně spouštět jak na simulátorech, tak na skutečném kvantovém hardwaru. Viděl/a jsi také, jak důležitý je pečlivý návrh Circuit pro minimalizaci chyb při práci se skutečnými kvantovými zařízeními, a také strategie pro škálování Circuit na větší počty qubitů, zejména na příkladu GHZ stavu. Dále ses seznámil/a s různými technikami kódování klasických informací do kvantových stavů, včetně amplitudového kódování a angle encodingu. S tím vším jsi plně vybaven/a k přechodu na další část a k zahájení práce s kvantovými algoritmy.

Instalace Qiskit Code Assistant ve VSCode

Klikni na odkaz a postupuj podle pokynů.

Bonus: Kvantová teleportace

Když uslyšíš pojem kvantová teleportace, možná si představíš futuristickou sci-fi technologii, která rozloží objekt na jednom místě a znovu ho sestaví někde daleko. Kvantová teleportace ale nevypadá vůbec takto. Ve skutečnosti se neteleportuje hmota, ale informace.

Kvantová teleportace umožňuje přenos kvantového stavu qubitu z jednoho místa na druhé. Přestože se tento přenos zdá být okamžitý, neporušuje zákony fyziky. Jak je to možné? Pojďme se podívat blíže!

Kvantová teleportace je protokol, který umožňuje odesílateli (Alici) přenést stav ψ|\psi\rangle qubitu q příjemci (Bobovi) pomocí dvou klíčových zdrojů: sdíleného provázaného páru qubitů a a b a dvou bitů klasické komunikace c0 a c1.

Protokol v zásadě potřebuje:

  • q: Alicin qubit, původně ve stavu ψ|\psi\rangle, který chceme teleportovat.
  • a: Alicina polovina sdíleného provázaného páru.
  • b: Bobova polovina sdíleného provázaného páru.
  • c0, c1: Klasické bity pro uložení výsledků Aliciných měření.

A jak to funguje? Postup je následující:

  1. Připrav Alicin stav ψ|\psi\rangle na q. Pro ověření vytvoříme konkrétní stav, například +|+\rangle.
  2. Vytvoř provázání: Vygeneruj Bellův pár mezi a a b.
  3. Aliciny operace: Alice provede „Bellovo měření" na svých dvou qubitech (q a a) a uloží klasické výsledky do c0 a c1.
  4. Klasická komunikace: Alice pošle Bobovi své dva klasické bity (c0, c1).
  5. Bobovy opravy: Bob aplikuje na svůj qubit (b) specifické kvantové Gate (X a/nebo Z) podmíněně hodnotami c0 a c1, které obdržel.

Pokud je vše provedeno správně, Bobův qubit b skončí ve stavu ψ|\psi\rangle — původním stavu Alicina q!

Pro podrobnější vysvětlení a průzkum kvantové teleportace, včetně matematického zdůvodnění, proč tento protokol funguje, se můžeš podívat na výukové zdroje IBM Quantum: Quantum Teleportation. Toto je součástí kurzu Basics of Quantum Information.


import matplotlib.pyplot as plt
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, plot_bloch_multivector

# Define individual quantum registers for each qubit
q = QuantumRegister(1, name='q') # message qubit
a = QuantumRegister(1, name='a') # Alice's entangled qubit
b = QuantumRegister(1, name='b') # Bob's entangled qubit

# Classical register for Alice's measurements
cr_alice = ClassicalRegister(2, name='c_alice')

# Create quantum circuit
teleport_qc = QuantumCircuit(q, a, b, cr_alice, name='Teleportation')

# Step 1: Prepare message state |+⟩ on q
teleport_qc.h(q[0])
teleport_qc.barrier()

# Step 2: Create entanglement between a and b
teleport_qc.h(a[0])
teleport_qc.cx(a[0], b[0])
teleport_qc.barrier()

# Step 3: Alice's Bell measurement
teleport_qc.cx(q[0], a[0])
teleport_qc.h(q[0])
teleport_qc.barrier()

# Step 4: Alice measures q and a
teleport_qc.measure(q[0], cr_alice[0])
teleport_qc.measure(a[0], cr_alice[1])
teleport_qc.barrier()

# Step 5: Bob's conditional measurements
with teleport_qc.if_test((cr_alice[1], 1)):
teleport_qc.x(b[0])
with teleport_qc.if_test((cr_alice[0], 1)):
teleport_qc.z(b[0])

# Draw the circuit
teleport_qc.draw(output='mpl')

Quantum circuit diagram

Po provedení protokolu vyvstává klíčová otázka: jak ověříme, že teleportace proběhla úspěšně? Stav Bobova qubitu po protokolu nemůžeme přímo „vidět". Protože jsme však Alicin počáteční stav ψ|\psi\rangle připravili (zvolili jsme +|+\rangle), můžeme použít speciální typ simulace a zkontrolovat, zda Bobův qubit b skončil v tomtéž stavu.

Použijeme AerSimulator s save_statevector k ověření, zda Bobův qubit b skončí v Alicině původním stavu (+|+\rangle). Tento simulátor vypočítá výsledný vektor kvantového stavu a následně ho zobrazí pomocí plot_bloch_multivector, aby bylo možné vizualizovat Bobův qubit (b) v porovnání s Aliciným počátečním stavem (q).

# Simulate the teleportation circuit
sv_simulator = AerSimulator(method='statevector')
teleport_qc_sv = teleport_qc.copy()
teleport_qc_sv.save_statevector()

# Execute the circuit on the statevector simulator
job_sv = sv_simulator.run(teleport_qc_sv)
result_sv = job_sv.result()

# Get the final statevector
final_statevector = result_sv.get_statevector()
print("Visualizing final qubit states:")
display(plot_bloch_multivector(final_statevector))
print("Note that Alice's qubits have collapsed to |00⟩, |01⟩, |10⟩, or |11⟩, while Bob's qubit is in the original state |+⟩.")
Visualizing final qubit states:

Quantum circuit diagram

Note that Alice's qubits have collapsed to |00⟩, |01⟩, |10⟩, or |11⟩, while Bob's qubit is in the original state |+⟩.

Jak je z vizualizace patrné, první dva qubity (patřící Alici) se zhroutily do hodnoty 0 nebo 1. Mezitím třetí qubit (patřící Bobovi), zobrazený ve třetí Blochově sféře, míří podél osy x, což naznačuje, že se nachází ve stavu +|+\rangle — úspěšně jsme tedy implementovali protokol kvantové teleportace!

Shrnutí

V tuto chvíli je vhodné stručně shrnout, čeho jsme dosáhli:

  • Alice přenesla Bobovi neznámý kvantový stav.
  • Nebyla přenesena žádná fyzická částice.
  • Původní stav na Alicině qubitu byl zničen, v souladu s teorémem o neklonování.

Kvantová teleportace však stále vyžaduje klasickou komunikaci (výsledky Aliciných měření zaslané Bobovi), a to vysvětluje, proč tento proces neumožňuje přenos informací rychlejší než světlo a je plně v souladu se všemi známými zákony fyziky.